#include <linux/fs.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include <linux/ctype.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include "wmark_dev.h"
#include <linux/dma-buf.h>
#include <linux/ion.h>
#include <linux/msm_ion.h>
#include <linux/list.h>
#include <linux/mutex.h>

#undef PDEBUG /* undef it, just in case */
#ifdef WMARK_DEBUG
#ifdef __KERNEL__
#define PDEBUG(fmt, args...) printk(KERN_DEBUG "DEMO: " fmt, ##args)
#else //usr space
#define PDEBUG(fmt, args...) fprintf(stderr, fmt, ##args)
#endif
#else
#define PDEBUG(fmt, args...) /* not debugging: nothing */
#endif

#undef PDEBUGG
#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */

#define debug(fmt, arg...)                                                     \
	printk(KERN_DEBUG "%s %d " fmt "\n", __func__, __LINE__, ##arg)

#define error(fmt, arg...)                                                     \
	printk(KERN_ERR "%s %d " fmt "\n", __func__, __LINE__, ##arg)

#define info(fmt, arg...)                                                     \
	printk("%s %d " fmt "\n", __func__, __LINE__, ##arg)


#define WMARK_DEV_NAME "wmark_dev"

static struct miscdevice wmark_dev;

typedef struct {
	buf_handle_t data;
	struct list_head list;
} buf_list_t;

typedef struct {
	struct mutex mutex;
	buf_list_t wmark_r_list_head;
	buf_list_t wmark_w_list_head;
	wait_queue_head_t r_wait;//等待队列的头
    wait_queue_head_t w_wait;//等待队列的头
} dma_bufs_manager_t;

dma_bufs_manager_t global_dma_bufs_manager;

//函数申明
ssize_t wmark_read(struct file *filp, char __user *buf, size_t count,
		   loff_t *f_pos);
ssize_t wmark_write(struct file *filp, const char __user *buf, size_t count,
		    loff_t *f_pos);
loff_t wmark_llseek(struct file *filp, loff_t off, int whence);
long wmark_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);

MODULE_AUTHOR("leo");
MODULE_LICENSE("Dual BSD/GPL");

wmark_dev_ioctl_t row_data;
wmark_dev_ioctl_t send_data;

wmark_dev_ioctl_t bufs_row_data;
wmark_dev_ioctl_t bufs_send_data;

//static unsigned char wmark_inc=0;

struct dma_buf *dmabuf = NULL;
struct dma_buf *dmabufs[DMA_BUFS_MAX_NUM] = { 0 };

int wmark_open(struct inode *inode, struct file *filp)
{
	//struct wmark_dev *dev;

	//if(wmark_inc>0)return -ERESTARTSYS;
	//wmark_inc++;

	//dev = container_of(inode->i_cdev, struct wmark_dev, cdev);
	//filp->private_data = dev;
	filp->private_data = &global_dma_bufs_manager;

	return 0;
}

int wmark_release(struct inode *inode, struct file *filp)
{
	//wmark_inc--;
	return 0;
}

// 入
int push(buf_list_t *new, struct list_head *head)
{
	if (!new || !head) {
		error("fail");
		return -1;
	}
	list_add(&new->list, head);
	return 0;
}

// 出
buf_list_t * pop(struct list_head *head)
{
	buf_list_t *ret = NULL;
	// struct list_head *tmp_node = NULL;

	if (!head) {
		error("fail");
		return ret;
	}

	if (list_empty(head)) {
		return NULL;
	}

#if 0
	list_for_each(tmp_node, head) {
		break;
	}

	if (tmp_node) {
		ret = container_of(tmp_node, buf_list_t, list);
		list_del(tmp_node);
	}
#else
	ret = list_first_entry(head, buf_list_t, list);
	list_del(head->next);
#endif

	return ret;
}

int export_set_use_buf_index(void __user *argp, dma_bufs_manager_t *dev)
{
	buf_list_t *_new = NULL;
	buf_handle_t kernel_recv;
	debug();
	
	mutex_lock(&dev->mutex);
	_new = (buf_list_t *) kmalloc( sizeof(buf_list_t), GFP_KERNEL);
	if (!_new) {
		error("fail");
		mutex_unlock(&dev->mutex);
		return -1;
	}
	INIT_LIST_HEAD(&_new->list);
	copy_from_user((void *)&kernel_recv, (void __user *)argp,
			       sizeof(kernel_recv));
	_new->data.info.test1 = kernel_recv.info.test1;
	_new->data.index = kernel_recv.index;
	debug("push -- index=%d test1=%d", _new->data.index, _new->data.info.test1);
	push(_new, &dev->wmark_r_list_head.list); // 加入可读的 列表里，

	wake_up_interruptible(&dev->r_wait); // 通知 import 读

	
	mutex_unlock(&dev->mutex);

	return 0;
}

int export_get_free_buf_index(void __user *argp, dma_bufs_manager_t *dev)
{
	buf_handle_t kernel_send;
	buf_list_t *_buf;
	debug();
	
	mutex_lock(&dev->mutex);
	
	_buf = pop(&dev->wmark_w_list_head.list); // 从 写 队列中拿 一个 free 的buf
	if (!_buf) {
		debug("pop == NULL");
		mutex_unlock(&dev->mutex);
		return -EAGAIN;
	}

	kernel_send.index = _buf->data.index;
	kernel_send.info.test1 = _buf->data.info.test1;
	debug("pop -- index=%d test1=%d", kernel_send.index, kernel_send.info.test1);
	kfree(_buf);

	copy_to_user((void __user *)argp, (void *)&kernel_send,
			     sizeof(kernel_send));
	mutex_unlock(&dev->mutex);

	return 0;
}

int import_set_free_buf_index(void __user *argp, dma_bufs_manager_t *dev)
{
	buf_list_t *_new = NULL;
	buf_handle_t kernel_recv;
	debug();
	
	mutex_lock(&dev->mutex);
	_new = (buf_list_t *) kmalloc( sizeof(buf_list_t), GFP_KERNEL);
	if (!_new) {
		error("fail");
		mutex_unlock(&dev->mutex);
		return -1;
	}
	INIT_LIST_HEAD(&_new->list);
	copy_from_user((void *)&kernel_recv, (void __user *)argp,
			       sizeof(kernel_recv));
	_new->data.info.test1 = kernel_recv.info.test1;
	_new->data.index = kernel_recv.index;
	debug("push -- index=%d test1=%d", kernel_recv.index, kernel_recv.info.test1);
	push(_new, &dev->wmark_w_list_head.list); // 加入可写的 列表里，

	wake_up_interruptible(&dev->w_wait); // 通知 exprot 可以写

	mutex_unlock(&dev->mutex);

	return 0;
}

int import_get_use_buf_index(void __user *argp, dma_bufs_manager_t *dev)
{
	buf_handle_t kernel_send;
	buf_list_t *_buf;
	debug();
	
	mutex_lock(&dev->mutex);
	
	_buf = pop(&dev->wmark_r_list_head.list); // 从 写 队列中拿 一个 free 的buf
	if (!_buf) {
		debug("pop == NULL");
		mutex_unlock(&dev->mutex);
		return -EAGAIN;
	}

	kernel_send.index = _buf->data.index;
	kernel_send.info.test1 = _buf->data.info.test1;
	kfree(_buf);
	debug("pop -- index=%d test1=%d", kernel_send.index, kernel_send.info.test1);
	copy_to_user((void __user *)argp, (void *)&kernel_send,
			     sizeof(kernel_send));
	mutex_unlock(&dev->mutex);

	return 0;
}

long wmark_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	dma_bufs_manager_t *dev = filp->private_data;
	void __user *argp = (void __user *)arg;
	int fd = -1;
	int i = -1;

	switch (cmd) {
	case SET_FLAG_CMD:
		copy_from_user((void *)&row_data, (void __user *)arg,
			       sizeof(row_data));
		debug("SET_FLAG_CMD, buf_fd=%d buf_len=%d", row_data.buf_fd,
		      row_data.buf_len);
		dmabuf = dma_buf_get(row_data.buf_fd);
		break;

	case GET_FLAG_CMD:
		if (!dmabuf) {
			debug("fail!");
			return -EFAULT;
		}
		fd = dma_buf_fd(dmabuf, O_CLOEXEC);
		if (fd < 0) {
			debug("dma get fd fail, *fd=%d\n", fd);
			return -EINVAL;
		}
		send_data.buf_fd = fd;
		send_data.buf_len = row_data.buf_len;
		copy_to_user((void __user *)arg, (void *)&send_data,
			     sizeof(send_data));
		debug("GET_FLAG_CMD fd=%d len=%d", send_data.buf_fd,
		      send_data.buf_len);
		break;

	case SET_BUFS_CMD:
		debug("SET_BUFS_CMD ->");
		copy_from_user((void *)&bufs_row_data, (void __user *)arg,
			       sizeof(bufs_row_data));
		for (i = 0; i < DMA_BUFS_MAX_NUM; i++) {
			fd = bufs_row_data.fds[i].fd;
			dmabufs[i] = dma_buf_get(fd);
			debug("SET_BUFS_CMD dma-buf=%p fd=%d", dmabufs[i], fd);
		}
		break;

	case GET_BUFS_CMD:
		debug("GET_BUFS_CMD ->");
		if (!dmabufs[0]) {
			debug("GET_BUFS_CMD fail!");
			return -EFAULT;
		}
		for (i = 0; i < DMA_BUFS_MAX_NUM; i++) {
			fd = dma_buf_fd(dmabufs[i], O_CLOEXEC);
			if (fd < 0) {
				debug("GET_BUFS_CMD dma get fd fail, *fd=%d\n", fd);
				return -EINVAL;
			}
			debug("GET_BUFS_CMD : dma-buf=%p fd=%d", dmabufs[i], fd);
			bufs_send_data.fds[i].fd = fd;
			bufs_send_data.fds[i].index = i;
		}

		bufs_send_data.buf_len = bufs_row_data.buf_len;
		copy_to_user((void __user *)arg, (void *)&bufs_send_data,
			     sizeof(bufs_send_data));
		debug("GET_FLAG_CMD <-");
		break;

	case SET_USE_BUF_INDEX_EXPORT: // 3
		debug("SET_USE_BUF_INDEX_EXPORT ->");
		export_set_use_buf_index(argp, dev);
		break;
	
	case GET_FREE_BUF_INDEX_EXPORT: // 2
		debug("GET_FREE_BUF_INDEX_EXPORT ->");
		export_get_free_buf_index(argp, dev);
		break;
	
	case SET_FREE_BUF_INDEX_IMPORT: // 1
		debug("SET_FREE_BUF_INDEX_IMPORT ->");
		import_set_free_buf_index(argp, dev);
		break;
	
	case GET_USE_BUF_INDEX_IMPORT: // 4
		debug("GET_USE_BUF_INDEX_IMPORT ->");
		import_get_use_buf_index(argp, dev);
		break;
	default:
		error("default ->");
		return -EFAULT;
		break;
	}
	return 0;
}

static unsigned int wmark_poll(struct file *filp, poll_table *wait)
{
    unsigned int mask = 0;
	dma_bufs_manager_t *dev = filp->private_data;
    // struct globalfifo_dev *dev = filp->private_data ;

    mutex_lock(&dev->mutex);

    poll_wait(filp, &dev->r_wait, wait);
    poll_wait(filp, &dev->w_wait, wait);

	// 如果 读 列表 非空， 代表 import 可以 获得 use (填充好有用数据) 的 buf.  可读
    if(!list_empty(&dev->wmark_r_list_head.list)){
        mask |= POLLIN | POLLRDNORM;
    }

	// 如果 写 列表 非空， 代表 exprot 可以 获得 free 的 buf.   可写
    if(!list_empty(&dev->wmark_w_list_head.list)){
        mask |= POLLOUT | POLLWRNORM;
    }

    mutex_unlock(&dev->mutex);
    return mask;
}

struct file_operations wmark_fops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = wmark_ioctl,
	.open = wmark_open,
	.release = wmark_release,
	.poll = wmark_poll,
};

static struct miscdevice wmark_dev = {
	.fops = &wmark_fops,
	.minor = MISC_DYNAMIC_MINOR,
	.name = WMARK_DEV_NAME,
};

static int __init wmark_init(void)
{
	int ret = -1;
	debug();
	ret = misc_register(&wmark_dev);
	if (ret) {
		debug("fail misc wmark ion register.");
		goto fail;
	}

	INIT_LIST_HEAD(&global_dma_bufs_manager.wmark_r_list_head.list);
	INIT_LIST_HEAD(&global_dma_bufs_manager.wmark_w_list_head.list);
	mutex_init(&global_dma_bufs_manager.mutex);
	init_waitqueue_head(&global_dma_bufs_manager.r_wait);//初始化等待队列头
    init_waitqueue_head(&global_dma_bufs_manager.w_wait);

	return 0;
fail:
	return -ENOMEM;
}
module_init(wmark_init);

static void __exit wmark_exit(void)
{
	misc_deregister(&wmark_dev);
	debug();
}
module_exit(wmark_exit);
